﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace _001_TXT_小说爬虫
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// html解析对象
        /// </summary>
        private HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();

        /// <summary>
        /// 执行书籍写入的子线程
        /// </summary>
        private Thread MyThread;

        private delegate void WriteMessage(string msg);
        WriteMessage writeMessage;

        public Form1()
        {
            InitializeComponent();
        }

        #region 控件事件
        private void Form1_Load(object sender, EventArgs e)
        {
            //爱搜书网站
            this.BookUrl.Text = "https://www.isoshu.com/bookinfo/2694/";
            writeMessage = new WriteMessage(SetRichText);
        }

        private void LoadBtn_Click(object sender, EventArgs e)
        {
            try
            {
                //获取文件存放路径
                FolderBrowserDialog folder = new FolderBrowserDialog();
                if (folder.ShowDialog() == DialogResult.OK)
                {
                    this.LoggerRich.Clear();
                    this.LoggerRich.Focus();
                    MyThread = new Thread(() => DoWork(this.BookUrl.Text, folder.SelectedPath));
                    MyThread.Start();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void StopBtn_Click(object sender, EventArgs e)
        {
            MyThread.Abort();
            this.Invoke(writeMessage, "书籍写入已中止！");
        }
        #endregion

        #region 线程任务
        /// <summary>
        /// 通过委托调用的控件日志写入
        /// </summary>
        /// <param name="msg"></param>
        public void SetRichText(string msg)
        {
            this.LoggerRich.AppendText(msg);
        }

        /// <summary>
        /// 线程开启函数
        /// </summary>
        /// <param name="bookUrl"></param>
        /// <param name="folderPath"></param>
        public void DoWork(string bookUrl, string folderPath)
        {
            //书籍首页
            this.Invoke(writeMessage, "开始写入书籍...\n");
            string bookIndex = HttpMethod.HttpGet(bookUrl);
            htmlDoc.LoadHtml(bookIndex);
            string bookName = htmlDoc.DocumentNode.SelectNodes("//h1[@class='bookTitle']").First().InnerText;
            //书籍目录
            this.Invoke(writeMessage, "开始获取书籍目录...\n");
            string bookIndexUrl = "http://" + bookUrl.Split('/')[2];
            Dictionary<string, string> menuDic = GetMenuWithUrl(bookIndexUrl, "//dd[@class='col-md-3']");
            //书籍录入
            this.Invoke(writeMessage, "开始写入小说：" + bookName + "\n");
            WriteBook(folderPath, bookName, menuDic);
        }
        #endregion

        #region 书籍正文写入
        /// <summary>
        /// 书籍写入
        /// </summary>
        /// <param name="folderPath"></param>
        /// <param name="bookName"></param>
        /// <param name="bookMenu"></param>
        private void WriteBook(string folderPath, string bookName, Dictionary<string, string> menuDic)
        {
            //创建书籍
            string allPath = folderPath + "\\" + bookName + ".txt";
            CreateBook(allPath);
            //遍历目录，写入内容
            int i = 1;
            foreach (var item in menuDic.Keys)
            {
                //获取章节内容
                string chapterContent = GetChapterContent(menuDic[item]);
                //写入文章内容
                WriteChapterContent(allPath, item + "\n" + chapterContent);
                this.Invoke(writeMessage, menuDic.Keys.Count + "/" + (i++) + " " + item + "\n");
            }
            this.Invoke(writeMessage, "书籍写入完成！");
        }

        /// <summary>
        /// 创建书籍
        /// </summary>
        /// <param name="bookName"></param>
        /// <param name="text"></param>
        private void CreateBook(string allPath)
        {
            FileStream fileStream = new FileStream(allPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            fileStream.Seek(0, SeekOrigin.Begin);
            fileStream.SetLength(0);
            fileStream.Close();
        }

        /// <summary>
        /// 获取章节内容
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private string GetChapterContent(string url)
        {
            string contentHtml = HttpMethod.HttpGet(url);
            htmlDoc.LoadHtml(contentHtml);
            string chapterContent = htmlDoc.DocumentNode.SelectNodes("//div[@id='htmlContent']").First().InnerText;
            chapterContent = chapterContent.Replace("&nbsp;", "").Replace("\r\r\n\r\r\n", "\n\n").
                Replace("\r\n百度搜索 三寸人间 爱搜书 或 三寸人间 isoshu 即可找到本书.", "").
                Replace("\r\n()", "");
            return chapterContent;
        }

        /// <summary>
        /// 文章内容写入
        /// </summary>
        /// <param name="path"></param>
        /// <param name="chapterContent"></param>
        private void WriteChapterContent(string allPath, string chapterContent)
        {
            FileStream fileStream = new FileStream(allPath, FileMode.Append);
            StreamWriter streamWriter = new StreamWriter(fileStream);
            streamWriter.WriteLine(chapterContent);
            streamWriter.Close();
            fileStream.Close();
        }

        #endregion

        #region htmlAglityPack
        //获取单标签对应属性
        public string GetHtmlAttributes(string filter, string property)
        {
            var attributes = htmlDoc.DocumentNode.SelectNodes(filter).First().Attributes;
            foreach (var item in attributes)
            {
                if (item.Name == property)
                {
                    return item.Value;
                }
            }
            return "";
        }

        //获取所有目录及其网址
        public Dictionary<string, string> GetMenuWithUrl(string bookIndexUrl, string filter)
        {
            Dictionary<string, string> menuDic = new Dictionary<string, string>();
            var selectNodes = htmlDoc.DocumentNode.SelectNodes(filter);
            foreach (var node in selectNodes)
            {
                var aLabel = node.ChildNodes.First();
                if (!menuDic.ContainsKey(aLabel.InnerText))
                {
                    menuDic.Add(aLabel.InnerText, bookIndexUrl + aLabel.Attributes.First().Value);
                }
            }
            return menuDic;
        }
        #endregion
    }
}
